#include "include/lib.h"
#include "include/cxxstd.h"
#include "memory/memory.h"
#include "ABIBase.h"
#include "ctors.h"
#include "rel.h"

struct ELFHeader
{
	unsigned char	ident[16];
	__extension__ enum {
		ET_EXEC = 2,
		ET_DYN = 3,
	} type : 16;
	unsigned short	machine;
	unsigned int	version;
	unsigned int	entry;  /* Entry point */
	ptrdiff_t		phoff;
	ptrdiff_t		shoff;
	unsigned int	flags;
	unsigned short	ehsize;
	unsigned short	phentsize;
	unsigned short	phnum;
	unsigned short	shentsize;
	unsigned short	shnum;
	unsigned short	shstrndx;
};

struct SectionHeader
{
	unsigned int	name;
	unsigned int	type;
	unsigned int	flags;
	ptr_t			addr;
	unsigned int	offset;
	unsigned int	size;
	unsigned int	link;
	unsigned int	info;
	unsigned int	addralign;
	unsigned int	entsize;
};

namespace zero
{
ABIBase* ABIBase::last;

void ABIBase::load(void)
{
	Rel::init(elf, pic, importedSym, rel);
	Ctors::init(ctors);
}

void ABIBase::unload(void)
{
}

extern "C" void* _zero_image_eh_frame_hdr;
extern "C" void* _zero_image_ctors;
extern "C" void* _zero_image_ctors_end;

ABIBase* ABIBase::create(const unsigned char* elfHeader, MallocHandle mh, FreeHandle fh)
{
	ABIBase* base = (ABIBase*) os_allocator(sizeof(ABIBase));
	if (!base)
		std::abort(NULL);
	base->newHandler = mh;
	base->deleteHandler = fh;
	if (elfHeader == NULL) { // OS initialization, also it's the stack bottom!.
		base->prev = NULL;
		base->pic = false;
		base->text.begin = 0;
		base->text.end = 0x10000;
		base->exHeader.begin = reinterpret_cast<ptr_t>(&_zero_image_eh_frame_hdr);
		base->ctors.begin = reinterpret_cast<ptr_t>(&_zero_image_ctors);
		base->ctors.end = reinterpret_cast<ptr_t>(&_zero_image_ctors_end);
		// Also, the ctors section of os is called here.
		Ctors::init(base->ctors);
	} else
		base->paserELF(elfHeader);
	return base;
}

void ABIBase::paserELF(const unsigned char* base)
{
	elf.begin = reinterpret_cast<ptr_t>(base);
	const ELFHeader* elfHeader = reinterpret_cast<const ELFHeader*>(elf.begin);
	pic = elfHeader->type == ELFHeader::ET_EXEC ? false : true;
	const SectionHeader* sectionHeader = reinterpret_cast<const SectionHeader*>(elf.begin + elfHeader->shoff);
	const char* strTable = reinterpret_cast<const char*>(elf.begin) + sectionHeader[elfHeader->shstrndx].offset;
	ptrdiff_t picrel = pic ? elf.begin : 0;
	codeEntry = reinterpret_cast<Entry>(picrel + elfHeader->entry);
#define _C(c) c, sizeof(c)
	for (int i = 0; i < elfHeader->shnum; i++) {
		if (bstrcmp(_C("text"), strTable + sectionHeader[i].name)) {
			text.begin = picrel + sectionHeader[i].addr;
			text.end = text.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("ctors"), strTable + sectionHeader[i].name)) {
			ctors.begin = picrel + sectionHeader[i].addr;
			ctors.end = ctors.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("rel"), strTable + sectionHeader[i].name)) {
			rel.begin = picrel + sectionHeader[i].addr;
			rel.end = rel.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C(".dynsym"), strTable + sectionHeader[i].name)) {
			importedSym.begin = picrel + sectionHeader[i].addr;
			importedSym.end = importedSym.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C(".eh_frame_hdr"), strTable + sectionHeader[i].name)) {
			exHeader.begin = picrel + sectionHeader[i].addr;
			exHeader.end = exHeader.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C(".eh_frame"), strTable + sectionHeader[i].name)) {
			unwindex.begin = picrel + sectionHeader[i].addr;
			unwindex.end = unwindex.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("cxxex"), strTable + sectionHeader[i].name)) {
			cxxex.begin = picrel + sectionHeader[i].addr;
			cxxex.end = cxxex.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("typeinfo"), strTable + sectionHeader[i].name)) {
			typeinfo.begin = picrel + sectionHeader[i].addr;
			typeinfo.end = typeinfo.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("data"), strTable + sectionHeader[i].name)) {
			data.begin = picrel + sectionHeader[i].addr;
			data.end = data.begin + sectionHeader[i].size;
		} else if (bstrcmp(_C("bss"), strTable + sectionHeader[i].name)) {
			bss.begin = picrel + sectionHeader[i].addr;
			bss.end = bss.begin + sectionHeader[i].size;
		} else {
			// abort();
		}
	}
}
}
// vim: foldmarker=<([{,}])> foldmethod=marker
